﻿using System.Reflection;
using Futuret.Core.Futuret;
using Futuret.Core.Utils;
using Futuret.Repositor.Base.Entity;
using Futuret.Repositor.Base.Interface;
using Futuret.Repositor.Config;

namespace Futuret.Repositor.Db;

/// <summary>
///  Author: Riva
///  Date: 2023/7/11 16:39 
///  Description: DbContext
/// </summary>
public static class DbContext
{
    //TODO 有待优化
    public static readonly List<DbOption> DbConfigs = AppInfo.GetInstance().configuration!
        .GetSection("DbConnectionsOption:DbConnOption").Get<List<DbOption>>()!;
    
    /// <summary>
    ///     SqlSugar 数据库实例
    /// </summary>
    public static readonly SqlSugarScope Db = new(
        DbConfigs.Adapt<List<ConnectionConfig>>()
        , db =>
        {
            //遍历配置的数据库
            DbConfigs.ForEach(it =>
            {
                var sqlsugarScope = db.GetConnectionScope(it.ConfigId); //获取当前库
                AopSetting(sqlsugarScope); //aop配置
                SetDbConfig(sqlsugarScope, it); //实体拓展配置
                FilterSetting(sqlsugarScope); //过滤器配置
            });
        });

    /// <summary>
    ///     实体拓展配置,自定义类型多库兼容
    /// </summary>
    /// <param name="db"></param>
    /// <param name="config"></param>
    private static void SetDbConfig(SqlSugarScopeProvider db, ConnectionConfig config)
    {
        config.InitKeyType = InitKeyType.Attribute;
        config.IsAutoCloseConnection = true;
        config.MoreSettings = new ConnMoreSettings
        {
            IsAutoRemoveDataCache = true,
            SqlServerCodeFirstNvarchar = true // 采用Nvarchar
        };
    }
    
    /// <summary>
    ///     过滤器设置
    /// </summary>
    /// <param name="db"></param>
    public static void FilterSetting(SqlSugarScopeProvider db)
    {
        // 配置实体假删除过滤器
        db.QueryFilter.AddTableFilter<IDeleteFilter>(u => u.IsDelete == false);
    }
    
    /// <summary>
    ///     Aop设置
    /// </summary>
    /// <param name="db">数据库</param>
    /// <param name="isDev">是否是开发环境</param>
    private static void AopSetting(ISqlSugarClient db, bool isDev = true)
    {
        var config = db.CurrentConnectionConfig;
        // 设置超时时间
        db.Ado.CommandTimeOut = 30;

        // 打印SQL语句
        db.Aop.OnLogExecuting = (sql, pars) =>
        {
            if (!isDev)
                return;
            if (sql.StartsWith("SELECT"))
            {
                Console.ForegroundColor = ConsoleColor.Green;
                WriteSqlLog($"查询{config.ConfigId}库操作");
            }

            if (sql.StartsWith("INSERT"))
            {
                Console.ForegroundColor = ConsoleColor.Blue;
                WriteSqlLog($"新增{config.ConfigId}库操作");
            }

            if (sql.StartsWith("UPDATE"))
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                WriteSqlLog($"修改{config.ConfigId}库操作");
            }

            if (sql.StartsWith("DELETE"))
            {
                Console.ForegroundColor = ConsoleColor.Red;
                WriteSqlLog($"删除{config.ConfigId}库操作");
            }

            Console.WriteLine(UtilMethods.GetSqlString(config.DbType, sql, pars));
            WriteSqlLog($"{config.ConfigId}库操作结束");
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine();
        };

        //异常
        db.Aop.OnError = ex =>
        {
            if (!isDev)
                return;
            if (ex.Parametres == null) return;
            Console.ForegroundColor = ConsoleColor.Red;
            var pars = db.Utilities.SerializeObject(
                ((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
            WriteSqlLog($"{config.ConfigId}库操作异常");
            Console.WriteLine(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n");
            Console.ForegroundColor = ConsoleColor.White;
        };

        //插入和更新过滤器
        db.Aop.DataExecuting = (oldValue, entityInfo) =>
        {
            switch (entityInfo.OperationType)
            {
                case DataFilterType.UpdateByObject:
                    UpdateByObject();
                    break;

                case DataFilterType.InsertByObject:
                    InsertByObject();
                    break;

                case DataFilterType.DeleteByObject:
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
            }

            return;

            void InsertByObject()
            {
                if (entityInfo.EntityColumnInfo.IsPrimarykey &&
                    entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
                {
                    var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);
                    if (id == null || (long)id == 0)
                        entityInfo.SetValue(IdUtil.GetGuidId());
                }

                if (entityInfo.PropertyName == nameof(BaseEntity.CreateTime))
                    entityInfo.SetValue(DateTime.Now);

                //TODO 判断是否登录\新增人
            }

            void UpdateByObject()
            {
                //更新时间
                if (entityInfo.PropertyName == nameof(BaseEntity.LastEditTime))
                    entityInfo.SetValue(DateTime.Now);
            }
        };
        //查询数据转换
        db.Aop.DataExecuted = (value, entity) => { };
    }

    /// <summary>
    ///     处理数据迁移
    /// </summary>
    /// <param name="repositorConfig"></param>
    public static void InitDb(DbOption repositorConfig)
    {
        if (!repositorConfig.IsInitDb)
            return;

        //TODO 建库 ：如果不存在创建数据库存在不会重复创建
        //TODO 注意 ：Oracle和个别国产库需不支持该方法，需要手动建库
        if (repositorConfig.DbType != DbType.Oracle)
            Db.DbMaintenance.CreateDatabase();

        //获取SqlSugar表
        var entityTypes = AppInfo.FindTypes(u =>
            u is { IsInterface: false, IsAbstract: false, IsClass: true } && u.IsDefined(typeof(SugarTable), false));
        var enumerable = entityTypes as Type[] ?? entityTypes.ToArray();
        if (!enumerable.Any()) return;

        foreach (var entityType in enumerable)
        {
            //判断是否有分库分表特性
            var splitTable = entityType.GetCustomAttribute<SplitTableAttribute>();
            if (splitTable == null)
                //更新表
                Db.CodeFirst.InitTables(entityType);
            else
                //分库更新表
                Db.CodeFirst.SplitTables().InitTables(entityType);
        }
    }

    private static void WriteSqlLog(string msg)
    {
        Console.WriteLine($"=============={msg}==============");
    }
}